import { enqueueConcurrentClassUpdate } from './ReactFiberConcurrentUpdates';
import { NoLanes, mergeLanes, NoLane, isSubsetOfLanes } from './ReactFiberLane';
import assign from 'shared/assign';
export const UpdateState = 0;

export function initialUpdateQueue(fiber) {
    // 创建一个新的更新队列
    const queue = {
        baseState: fiber.memoizedState, // 本次更新前当前的fiber的状态，更新会基于它进行计算状态
        firstBaseUpdate: null, // 本次更新前该fiber保存的上次跳过的更新链表头
        lastBaseUpdate: null, // 本次更新前该fiber保存的上次跳过的更新链表尾
        shared: {
            // pending是一个循环链表
            pending: null,
        },
    };
    fiber.updateQueue = queue;
};

export function createUpdate(lane) {
    const update = { tag: UpdateState, lane, next: null };
    return update;
};

export function enqueueUpdate(fiber, update, lane) {
    // 获取更新队列
    const updateQueue = fiber.updateQueue;
    // 获取共享队列
    const sharedQueue = updateQueue.shared;
    return enqueueConcurrentClassUpdate(fiber, sharedQueue, update, lane)
};

/**
 * 根据老状态和更新队列中的更新计算最新状态
 * @date 2023-03-08
 * @param {any} workInProgress 要计算的fiber
 * @returns {any}
 */
export function processUpdateQueue(workInProgress, nextProps, renderLanes) {
    const queue = workInProgress.updateQueue;
    // 老链表头
    let firstBaseUpdate = queue.firstBaseUpdate;
    // 老链表尾
    let lastBaseUpdate = queue.lastBaseUpdate;
    const pendingQueue = queue.shared.pending;
    // 合并新老链表为单链表
    if (pendingQueue !== null) {
        queue.shared.pending = null;
        // 新链表头
        const lastPendingUpdate = pendingQueue;
        // 新链表尾
        const firstPendingUpdate = lastPendingUpdate.next;
        // 剪开新链表
        lastPendingUpdate.next = null;
        if (lastBaseUpdate === null) {
            // 指向新的链表头
            firstBaseUpdate = firstPendingUpdate;
        } else {
            // 链接新老链表
            lastBaseUpdate.next = firstPendingUpdate;
        }
        lastBaseUpdate = lastPendingUpdate;
    }
    if (firstBaseUpdate !== null) {
        // 上次跳过的更新前的状态
        let newState = queue.baseState;
        // 尚未执行的更新的lane
        let newLanes = NoLanes;
        let newBaseState = null;
        let newFirstBaseUpdate = null;
        let newLastBaseUpdate = null;
        let update = firstBaseUpdate;
        do {
            // 获取此更新车道
            const updateLane = update.lane;
            // 如果说updateLane不是renderLanes的子集的话，说明本次渲染不需要处理这个更新，就是需要跳过此更新
            if (!isSubsetOfLanes(renderLanes, updateLane)) {
                // 把此更新克隆一份
                const clone = {
                    id: update.id,
                    lane: updateLane,
                    payload: update.payload,
                };
                // 说明新的跳过的base链表为空，说明当前这更新是第一个跳过的更新
                if (newLastBaseUpdate === null) {
                    // 让新的跳过的链表头和链表尾都指向这个第一跳过的更新
                    newFirstBaseUpdate = newLastBaseUpdate = clone;
                    // 保存新的baseState为此跳过更新时得到state
                    newBaseState = newState;
                } else {
                    newLastBaseUpdate = newLastBaseUpdate.next = clone;
                }
                // 如果有跳过的更新，就把跳过的更新所在的赛道合并到newLanes
                // 最后会把newLanes赋值给fiber.lanes
                newLanes = mergeLanes(newLanes, updateLane);
            } else {
                // 说明已经有逃过的更新了
                if (newLastBaseUpdate !== null) {
                    const clone = {
                        id: update.id,
                        lane: NoLane,
                        payload: update.payload,
                    };
                    newLastBaseUpdate = newLastBaseUpdate.next = clone;
                }
                newState = getStateFromUpdate(update, newState, nextProps);
            }
            update = update.next;
        } while (update);
        // 如果没有跳过的更新
        if (!newLastBaseUpdate) {
            newBaseState = newState;
        }
        queue.baseState = newBaseState;
        queue.firstBaseUpdate = newFirstBaseUpdate;
        queue.lastBaseUpdate = newLastBaseUpdate;
        workInProgress.lanes = newLanes;
        workInProgress.memoizedState = newState;
    }
};

function getStateFromUpdate(update, prevState, nextProps) {
    switch (update.tag) {
        case UpdateState:
            const { payload } = update;
            let partialState;
            if (typeof payload === 'function') {
                partialState = payload.call(null, prevState, nextProps);
            } else {
                partialState = payload;
            }
            return assign({}, prevState, partialState);
    }
};

export function cloneUpdateQueue(current, workInProgress) {
    const workInProgressQueue = workInProgress.updateQueue;
    const currentQueue = current.updateQueue;
    if (workInProgressQueue === currentQueue) {
        const clone = {
            baseState: currentQueue.baseState,
            firstBaseUpdate: currentQueue.firstBaseUpdate,
            lastBaseUpdate: currentQueue.lastBaseUpdate,
            shared: currentQueue.shared,
        }
        workInProgress.updateQueue = clone;
    }
}